package structure.timewheel;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.*;




public class Timer {



    /**
     * MARK 

     *  各层时间轮里面的buckets,和delayQueue里面 指向同一堆Bucket
     *  所以这里是有限元素的
     *  
     * 用这里来取出Bucket指向任务
     */
    protected static DelayQueue<Bucket> delayQueue = new DelayQueue<>();
    
    

    /**
     * 最底层的那个时间轮
     * 用这可以找到上层的时间轮
     */
    private static TimeWheel timeWheel=new TimeWheel(20, System.currentTimeMillis());

    /**
     * 真正干活的 可以有多个线程
     */
    private static ExecutorService workerThreadPool;

    /**
     * 推进时间的 触发干活的 只要一个线程
     */
    private static ExecutorService bossThreadPool;


    /**
     * 创建2个线程池
     * 一个干活的 可以多个线程
     * 一个驱动时间, 触发干活的 只要一个线程
     */
    static  {
        
        ThreadFactory timeWheelWorker = new ThreadFactoryBuilder()
                .setPriority(10)
                .setNameFormat("TimeWheelWorker")
                .build();
        workerThreadPool = Executors.newFixedThreadPool(100, timeWheelWorker);
        ThreadFactory timeWheelBoss = new ThreadFactoryBuilder().setPriority(10)
                .setNameFormat("TimeWheelBoss")
                .build();
        
        
        bossThreadPool = Executors.newFixedThreadPool(1, timeWheelBoss);
        bossThreadPool.execute(() -> {
            while (true) {
                //MARK 时光流逝 就一个线程驱动时间轮前进
                advanceClock(20);
            }
        });
    }

    /**
     * 将任务添加到时间轮
     * 如果已经到了, 直接指向
     */
    public static void doOrSaveDelayTask(TimedTask timedTask) {
        
        if (timeWheel.addTask(timedTask)) { 
            //加进去了, 说明时间没到
            return;
        }
        
        //时间已经到了 干活~~~
        workerThreadPool.submit(timedTask.getTask());
        
    }

    /**
     * 推进一下时间轮的指针，并且将delayQueue中的任务取出来再重新扔进去
     */
    private static void advanceClock(long timeout) {
        try {
            //MARK 为什么不直接用delayQueue? 因为时间复杂度高 不能放太多 这里放bucket
            //拿出里面到期的Bucket, 最多等待timeout微秒
            Bucket bucket = delayQueue.poll(timeout, TimeUnit.MILLISECONDS);
            
            //超时返回的, 都没有嫌疑到期要做的任务
            if (bucket == null) {
                return;
            }
            
            //时间轮指针走动
            long now = bucket.getExpire();
            timeWheel.advanceClock(now);
            
            //检查 bucket里面的任务, 真的就是时间到了就执行了,不然再入时间轮的某bucket[]
            bucket.flush();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
